04 Component container e ng-content

Próxima Atividade
  • Sugerir melhoria
Play
Current Time 0:00
/
Duration Time 0:00
Remaining Time -0:00
Stream TypeLIVE
Loaded: 0%
Progress: 0%
0:00
Fullscreen
00:00
Mute
Playback Rate
  • 2x
  • 1.5x
  • 1.2x
  • 1x
  • 0.8x
  • 0.7x
1x
Subtitles
  • subtitles off
Captions
  • captions off
Chapters
  • Chapters
HD
Desculpe-nos pelo transtorno, mas este video não pode ser carregado, tente novamente.

Transcrição

Assim como várias redes sociais possuem compartilhamento e exibição de fotos, seria interessante se houvesse um número de curtidas para cada uma delas. A boa notícia é que, se checarmos a interface que define a forma da API, na qual acessamos os dados retornados por ela, teremos likes e comments.

Eles se encontram vazios, mas se tivéssemos interações na nossa aplicação, estariam disponíveis. Queremos que tais informações sejam exibidas logo após as imagens. Como faremos isso?

O componente responsável pela renderização das imagens é photos.component.ts e, em seu template, colaremos as seguintes linhas logo após a tag de fechamento de <ap-photo>:

<i aria-hidden="true" class="fa fa-heart-o fa-1x mr-2"></i>{{ photo.likes }}
<i aria-hidden="true" class="fa fa-comment-o fa-1x mr-2 ml-2"></i>{{ photo.comments }}

Com isso, acrescentamos dois ícones de Font Awesome, um de coração e outro de balão de fala (para comentário), ambos com tamanho 1x. Também fazemos o Data binding por meio de interpolação, para photos.likes e photo.comments. Se salvarmos e voltarmos ao navegador, eles aparecem abaixo de cada imagem, como gostaríamos.

Mas isso pode ficar ainda melhor se incluirmos as linhas entre <div>s centralizadas:

<div class="text-center">
    <i aria-hidden="true" class="fa fa-heart-o fa-1x mr-2"></i>{{ photo.likes }}
    <i aria-hidden="true" class="fa fa-comment-o fa-1x mr-2 ml-2"></i>{{ photo.comments }}
</div>

Além disso, podemos melhorar a exibição de cada imagem e seus respectivos botões colocando-os em um card.

No Bootstrap 3, era preciso utilizar panels, mas a partir da versão posterior eles se tornaram cards.

A estrutura de um card com borda suave e conteúdo centralizado é a seguinte:

<div class="card border-light text-center">
    <h4 class="card-header">TITULO DO CARD</h4>
    <div class="card-block text-justify">
        <!-- aqui entra o conteúdo -->
    </div>
</div:

Um card pode ter cabeçalho com título, e um corpo com texto justificado. Iremos componentizar este card para podermos utilizá-lo em qualquer módulo, independentemente do projeto. Não podemos criá-lo dentro de "photos", pois assim o atrelaremos ao universo de imagens, e não é este o caso.

No entanto, cards são compartilháveis, podendo ser utilizados em outros locais e com outros módulos. Sendo assim, criaremos em "app" a pasta "shared", e nela, a pasta "components", dentro da qual teremos subpastas de todos os componentes compartilháveis pela aplicação. No caso, criaremos "card", que conterá card.component.ts e card.component.html.

No primeiro arquivo, teremos:

import { Component } from '@angular/core';

@Component({
    selector: 'ap-card',
    templateUrl: './card.component.html'
})
export class CardComponent { 
    @Input() title: string = '';
}

E em seu template:

<div class="card border-light text-center">
    <h4 class="card-header">{{ title }}</h4>
    <div class="card-block text-justify">
    </div>
</div>

Nele, o title é pego da inbound property de card.component.ts e, na <div> com card-block, deve-se pegar o conteúdo do card em si. Em seguida, também criaremos dentro de "card" o módulo card.module.ts, pois se este card será compartilhado em diversos locais, ele deverá ser importado. Porém, um componente não pode fazer parte da declaração de mais de um módulo.

Isso significa que, se "errors" e "photos" incluírem o card em declarations, teremos problemas, e o componente não poderá ser exportado ali por não ser um módulo. Então, mesmo que tenhamos um único componente, o transformaremos em módulo para conseguirmos importá-lo em todos os locais que precisarem utilizá-lo.

Assim, em card.module.ts, digitaremos e salvaremos o seguinte:

import { NgModule } from '@angular/core';
import { CardComponent } from './card.component';

@NgModule({
    declarations: [CardComponent],
    exports: [CardComponent]
})
export class CardModule { }

Como queremos utilizar este card em "photos > photos > photo-list.module.ts", teremos que acessar photos.module.ts e importar CardModule:

imports: [
    CommonModule,
    PhotoModule,
    CardModule
]

E por falar nisso, já vimos que todo módulo criado precisa importar CommonModule para que as diretivas do Angular funcionem corretamente. Em card.module.ts, então, teremos:

@NgModule({
    declarations: [CardComponent],
    exports: [CardComponent],
    imports: [CommonModule]
})

Recapitulando: o CardModule depende de CommonModule e exporta CardComponent. E em "photos > photo-list > photo-list.module.ts" indicamos que precisamos de CardModule, pois este componente exportado pelo card será utilizado por PhotosComponent.

Vamos acessar photos.component.html, incluir <ap-card> e mover as tags <ap-photo> e <div> para dentro dela. Por ora, nosso card não terá título, pois estamos utilizando-o por motivos de organização. Então, dentro do card exibimos a imagem, e incluiremos um padding de valor 1 para todos os lados.

<div *ngFor="let photo of cols" class="col-4">
    <ap-card>
        <ap-photo
            [url]="photo.url"
            [description]="photo.description">
        </ap-photo>
        <div class="text-center p-1">
            <i aria-hidden="true" class="fa fa-heart-o fa-1x mr-2"></i>{{ photo.likes }}
            <i aria-hidden="true" class="fa fa-comment-o fa-1x mr-2 ml-2"></i>{{ photo.comments }}
        </div>
    </ap-card>
</div>

Ao salvarmos e voltarmos ao navegador, teremos uma surpresa: nada é exibido. Podemos adicionar title="Flavio" em <ap-card>, e o texto será exibido em todos os cards, porém não seu conteúdo. Isto porque em nenhum momento indicamos que no componente criado em card.component.html deve estar o conteúdo que for colocado em <ap-card>. Para isso, utilizamos a diretiva ng-content:

<div class="card-block text-justify">
    <ng-content></ng-content>
</div>

Isso resolverá nosso problema e, se quisermos, podemos voltar à listagem em photos.component.html e incluir title="photo.description" com Data binding (caso contrário a string é que aparecerá): <ap-card [title]="photo.description">. Salvaremos as alterações, e teremos as imagens sendo exibidas com sucesso, cada qual com o título correspondente. Podemos deletar o título, que incluímos apenas para fins de teste.

Conseguimos criar nosso primeiro componente que aceita receber conteúdo entre abertura e fechamento de tags.

Tirar dúvida Próxima Atividade
ícone Angular parte 1: Fundamentos

Angular parte 1: Fundamentos

100%
Aula07 de 07

Lapidando ainda mais nossa aplicação

Atividades 4 de 14

  • 01 Projeto da aula anterior
  • 02 Submódulos 14min
  • 03 Integração com Font Awesome 09min
  • 04 Component container e ng-content 14min
  • 05 Desaparecimento de elementos
  • 06 Componentizando o filtro 08min
  • 07 Output property 09min
  • 08 Sobre output property
  • 09 Criando a primeira diretiva 06min
  • 10 Terminando a implementação da diretiva 11min
  • 11 Sobre diretivas
  • 12 Considerações finais 01min
  • 13 Consolidando seu conhecimento
  • 14 Projeto completo

Aulas

Outros Links

Fórum Trocar Curso
Gravatar de Marcos Katsumi Kay

Marcos Katsumi Kay 34.4k xp

Logo da Alura